﻿#region

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using ylyfDataCommon.Exceptions;
using ylyfDataConnectionManager.Command;
using ylyfDataConnectionManager.Common;
using ylyfDataEntityConverter.Common;

#endregion

namespace ylyfDataConnectionManager.Transaction
{
    public class HiDbTransaction : IDisposable
    {
        internal HiDbTransaction(HiDataBase DataBase, TransactionExceptionHandler ExceptionHandler = null)
        {
            db = DataBase;
            Connection = DataBase.CreateConnection();
            Connection.Open();

            Transaction = Connection.BeginTransaction();
            transactionExceptionHandler = ExceptionHandler;
        }

        private DbTransaction Transaction { get; set; }
        private HiDataBase db { get; }
        private DbConnection Connection { get; set; }
        private bool AutoResetWhenConnectionClose { get; set; }
        private ConnectionState State => Connection.State;
        public TransactionExceptionHandler transactionExceptionHandler { get; set; }

        public void Dispose()
        {
            Transaction?.Dispose();
            Connection?.Dispose();
            GC.SuppressFinalize(this);
        }

        private void CheckTransactionState()
        {
            if (CheckObjectState())
            {
                if (AutoResetWhenConnectionClose)
                {
                    Connection = db.CreateConnection();
                    Connection.Open();
                    Transaction = Connection.BeginTransaction();
                    return;
                }
                throw new TransactionIsOverException();
            }
        }

        private bool CheckObjectState()
        {
            return State == ConnectionState.Closed || State == ConnectionState.Broken;
        }

        private HiDbCommand ConfigCommand(HiDbCommand command)
        {
            CheckTransactionState();
            if (command == null)
                throw new NullReferenceException();
            command.Connection = Connection;
            command.Transaction = Transaction;
            return command;
        }


        private void ExecuteError(Exception e)
        {
            if (transactionExceptionHandler == null)
                throw e;
            if (transactionExceptionHandler.HandleException(e, this))
                throw e;
        }

        public void Rollback()
        {
            try
            {
                Transaction.Rollback();
                Transaction.Dispose();
                Connection.Close();
            }
            catch
            {
            }
        }

        public void Commit()
        {
            try
            {
                Transaction.Commit();
                Transaction.Dispose();
                Connection.Close();
            }
            catch
            {
            }
        }

        public HiDbTransactionChain BeginChain()
        {
            return new HiDbTransactionChain(this);
        }

        ~HiDbTransaction()
        {
            Dispose();
        }

        #region 在本事务中执行

        public int ExecuteNonQuery(DbCommand command)
        {
            try
            {
                HiDbCommand hicommand = ConfigCommand(new HiDbCommand(command, db));
                return hicommand.ExecuteNonQuery();
            }
            catch (Exception e)
            {
                ExecuteError(e);
                return -1;
            }
        }

        public int ExecuteNonQuery(HiDbCommand command)
        {
            try
            {
                return ConfigCommand(command).ExecuteNonQuery();
            }
            catch (Exception e)
            {
                ExecuteError(e);
                return -1;
            }
        }

        public int ExecuteNonQuery(string commandText, CommandType SqlType, DbParameter parameter = null)
        {
            try
            {
                var command = ConfigCommand(db.CreateHiDbCommandWithoutConnection(commandText, SqlType));
                if (parameter != null)
                    command.Parameters.Add(parameter);
                return command.ExecuteNonQuery();
            }
            catch (Exception e)
            {
                ExecuteError(e);
                return -1;
            }
        }

        public int ExecuteNonQuery(string commandText, CommandType SqlType, DbParameter[] parameters)
        {
            try
            {
                var command = ConfigCommand(db.CreateHiDbCommandWithoutConnection(commandText, SqlType));
                command.Parameters.Add(parameters);
                return command.ExecuteNonQuery();
            }
            catch (Exception e)
            {
                ExecuteError(e);
                return -1;
            }
        }

        public int SqlStringExecuteNonQuery(string commandText, DbParameter parameter = null)
        {
            return ExecuteNonQuery(commandText, CommandType.Text, parameter);
        }

        public int SqlStringExecuteNonQuery(string commandText, DbParameter[] parameters)
        {
            return ExecuteNonQuery(commandText, CommandType.Text, parameters);
        }

        public int StoredProcedureExecuteNonQuery(string commandText, DbParameter parameter = null)
        {
            return ExecuteNonQuery(commandText, CommandType.StoredProcedure, parameter);
        }

        public int StoredProcedureExecuteNonQuery(string commandText, DbParameter[] parameters)
        {
            return ExecuteNonQuery(commandText, CommandType.StoredProcedure, parameters);
        }

        public DataSet ExecuteDataSet(DbCommand command)
        {
            try
            {
                HiDbCommand hicommand = ConfigCommand(new HiDbCommand(command, db));
                return hicommand.ExecuteDataSet();
            }
            catch (Exception e)
            {
                ExecuteError(e);
                return null;
            }
        }

        public DataSet ExecuteDataSet(HiDbCommand command)
        {
            try
            {
                return ConfigCommand(command).ExecuteDataSet();
            }
            catch (Exception e)
            {
                ExecuteError(e);
                return null;
            }
        }


        public DataSet ExecuteDataSet(string commandText, CommandType SqlType, DbParameter parameter = null)
        {
            try
            {
                var command = ConfigCommand(db.CreateHiDbCommandWithoutConnection(commandText, SqlType));
                if (parameter != null)
                    command.AddParameter(parameter);
                return command.ExecuteDataSet();
            }
            catch (Exception e)
            {
                ExecuteError(e);
                return null;
            }
        }

        public DataSet ExecuteDataSet(string commandText, CommandType SqlType, DbParameter[] parameters)
        {
            try
            {
                var command = ConfigCommand(db.CreateHiDbCommandWithoutConnection(commandText, SqlType));
                command.AddParameter(parameters);
                return command.ExecuteDataSet();
            }
            catch (Exception e)
            {
                ExecuteError(e);
                return null;
            }
        }

        public DataSet SqlStringExecuteDataSet(string commandText, DbParameter parameter = null)
        {
            return ExecuteDataSet(commandText, CommandType.Text, parameter);
        }

        public DataSet SqlStringExecuteDataSet(string commandText, DbParameter[] parameters)
        {
            return ExecuteDataSet(commandText, CommandType.Text, parameters);
        }

        public DataSet StoredProcedureExecuteDataSet(string commandText, DbParameter parameter = null)
        {
            return ExecuteDataSet(commandText, CommandType.StoredProcedure, parameter);
        }

        public DataSet StoredProcedureExecuteDataSet(string commandText, DbParameter[] parameters)
        {
            return ExecuteDataSet(commandText, CommandType.StoredProcedure, parameters);
        }


        public DataTable ExecuteDataTable(DbCommand command)
        {
            try
            {
                HiDbCommand hicommand = ConfigCommand(new HiDbCommand(command, db));
                return hicommand.ExecuteDataTable();
            }
            catch (Exception e)
            {
                ExecuteError(e);
                return null;
            }
        }

        public DataTable ExecuteDataTable(HiDbCommand command)
        {
            try
            {
                HiDbCommand hicommand = ConfigCommand(command);
                return hicommand.ExecuteDataTable();
            }
            catch (Exception e)
            {
                ExecuteError(e);
                return null;
            }
        }

        public DataTable ExecuteDataTable(string commandText, CommandType SqlType, DbParameter parameter = null)
        {
            try
            {
                var command = ConfigCommand(db.CreateHiDbCommandWithoutConnection(commandText, SqlType));
                if (parameter != null)
                    command.AddParameter(parameter);

                return command.ExecuteDataTable();
            }
            catch (Exception e)
            {
                ExecuteError(e);
                return null;
            }
        }

        public DataTable ExecuteDataTable(string commandText, CommandType SqlType, DbParameter[] parameters)
        {
            try
            {
                var command = ConfigCommand(db.CreateHiDbCommandWithoutConnection(commandText, SqlType));
                command.AddParameter(parameters);
                return command.ExecuteDataTable();
            }
            catch (Exception e)
            {
                ExecuteError(e);
                return null;
            }
        }

        public DataTable SqlStringExecuteDataTable(string commandText, DbParameter parameter = null)
        {
            return ExecuteDataTable(commandText, CommandType.Text, parameter);
        }

        public DataTable SqlStringExecuteDataTable(string commandText, DbParameter[] parameters)
        {
            return ExecuteDataTable(commandText, CommandType.Text, parameters);
        }

        public DataTable StoredProcedureExecuteDataTable(string commandText, DbParameter parameter = null)
        {
            return ExecuteDataTable(commandText, CommandType.StoredProcedure, parameter);
        }

        public DataTable StoredProcedureExecuteDataTable(string commandText, DbParameter[] parameters)
        {
            return ExecuteDataTable(commandText, CommandType.StoredProcedure, parameters);
        }

        public List<T> ExecuteEntity<T>(DbCommand command, EntityConverter<T> converter = null) where T : new()
        {
            try
            {
                HiDbCommand hicommand = ConfigCommand(new HiDbCommand(command, db));
                return hicommand.ExecuteEntity(converter);
            }
            catch (Exception e)
            {
                ExecuteError(e);
                return null;
            }
        }

        public List<T> ExecuteEntity<T>(HiDbCommand command, EntityConverter<T> converter = null) where T : new()
        {
            try
            {
                return ConfigCommand(command).ExecuteEntity(converter);
            }
            catch (Exception e)
            {
                ExecuteError(e);
                return null;
            }
        }

        public List<T> ExecuteEntity<T>(string commandText, CommandType SqlType, EntityConverter<T> converter = null)
            where T : new()
        {
            try
            {
                var command = ConfigCommand(db.CreateHiDbCommand(commandText, SqlType));
                return command.ExecuteEntity(converter);
            }
            catch (Exception e)
            {
                ExecuteError(e);
                return null;
            }
        }

        public List<T> ExecuteEntity<T>(string commandText, CommandType SqlType, DbParameter parameter,
            EntityConverter<T> converter = null) where T : new()
        {
            try
            {
                var command = ConfigCommand(db.CreateHiDbCommand(commandText, SqlType));
                command.AddParameter(parameter);
                return command.ExecuteEntity(converter);
            }
            catch (Exception e)
            {
                ExecuteError(e);
                return null;
            }
        }

        public List<T> ExecuteEntity<T>(string commandText, CommandType SqlType, DbParameter[] parameters,
            EntityConverter<T> converter = null) where T : new()
        {
            try
            {
                var command = ConfigCommand(db.CreateHiDbCommand(commandText, SqlType));
                command.AddParameter(parameters);
                return command.ExecuteEntity(converter);
            }
            catch (Exception e)
            {
                ExecuteError(e);
                return null;
            }
        }

        public List<T> SqlStringExecuteEntity<T>(string commandText, EntityConverter<T> converter = null)
            where T : new()
        {
            return ExecuteEntity(commandText, CommandType.Text, converter);
        }

        public List<T> SqlStringExecuteEntity<T>(string commandText, DbParameter parameter,
            EntityConverter<T> converter = null) where T : new()
        {
            return ExecuteEntity(commandText, CommandType.Text, parameter, converter);
        }

        public List<T> SqlStringExecuteEntity<T>(string commandText, DbParameter[] parameters,
            EntityConverter<T> converter = null) where T : new()
        {
            return ExecuteEntity(commandText, CommandType.Text, parameters, converter);
        }

        public List<T> StoredProcedureExecuteEntity<T>(string commandText, EntityConverter<T> converter = null)
            where T : new()
        {
            return ExecuteEntity(commandText, CommandType.StoredProcedure, converter);
        }

        public List<T> StoredProcedureExecuteEntity<T>(string commandText, DbParameter parameter,
            EntityConverter<T> converter = null) where T : new()
        {
            return ExecuteEntity(commandText, CommandType.StoredProcedure, parameter, converter);
        }

        public List<T> StoredProcedureExecuteEntity<T>(string commandText, DbParameter[] parameters,
            EntityConverter<T> converter = null) where T : new()
        {
            return ExecuteEntity(commandText, CommandType.StoredProcedure, parameters, converter);
        }

        public object ExecuteScalar(HiDbCommand command)
        {
            try
            {
                return ConfigCommand(command).ExecuteScalar();
            }
            catch (Exception e)
            {
                ExecuteError(e);
                return null;
            }
        }


        public object ExecuteScalar(DbCommand command)
        {
            try
            {
                HiDbCommand hicommand = ConfigCommand(new HiDbCommand(command, db));
                return hicommand.ExecuteScalar();
            }
            catch (Exception e)
            {
                ExecuteError(e);
                return null;
            }
        }

        public object ExecuteScalar(string commandText, CommandType SqlType, params DbParameter[] parameters)
        {
            try
            {
                var command = ConfigCommand(db.CreateHiDbCommand(commandText, SqlType));
                command.AddParameter(parameters);
                return command.ExecuteScalar();
            }
            catch (Exception e)
            {
                ExecuteError(e);
                return null;
            }
        }

        //public object SqlStringExecuteScalar(string commandText, DbParameter parameter = null)
        //{
        //    return ExecuteDataTable(commandText, CommandType.Text, parameter);
        //}

        public object SqlStringExecuteScalar(string commandText, params DbParameter[] parameters)
        {
            return ExecuteDataTable(commandText, CommandType.Text, parameters);
        }


        public object StoredProcedureExecuteScalar(string commandText, params DbParameter[] parameters)
        {
            return ExecuteDataTable(commandText, CommandType.StoredProcedure, parameters);
        }

        #endregion
    }
}